#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <stdio.h>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

const int gCreater = 1;
const int gUser = 2;

const std::string gpathname = "/home/lj/111/lesson24/4.shm";
const int gproj_id = 0x66;
const int gShmSize = 4096; // 建议n * 4096, 不管多大，操作系统只会给4096的整数倍，但是你如果没申请4096的整数倍，你把你的size用超了容易出问题。

class Shm
{
private:
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetCommShm(key_t key, int size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("ftok");
        }
        return shmid;
    }
    std::string RoleToString()
    {
        if (_who == gCreater)    return "Creater";
        else if (_who == gUser)  return "User";
        return "None";
    }
    void* AttachShm()
    {
        void* shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmat");
        }
        else
            std::cout << "who: " << RoleToString() << " attach shm..." << std::endl;
        return shmaddr;
    }
    void DetachShm(void* shmaddr)
    {
        if (shmaddr == nullptr) return;
        shmdt(shmaddr);
        std::cout << "who: " << RoleToString() << " Detach shm..." << std::endl;
    }
public:
    Shm(const std::string pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _shmaddr(nullptr)
    {
        _key = GetCommKey();
        if (_who == gCreater)   GetShmForCreater(); // 先把共享内存申请出来
        else if (_who == gUser) GetShmForUser();
        _shmaddr = AttachShm(); // 再调用接口获取共享内存的地址
        if (_who == gCreater)   Zero(); // 把这个用于通信的共享内存区域块初始化
        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "key: " << ToHex() << std::endl;
    }
    ~Shm()
    {
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            std::cout << "shm remove done..." << std::endl;
        }
        DetachShm(_shmaddr);
    }
    std::string ToHex()
    {
        char buffer[128]{};
        snprintf(buffer, sizeof(buffer), "0x%x", _key);
        return buffer;
    }
    bool GetShmForCreater()
    {
        if (_who == gCreater)
        {
            _shmid = GetCommShm(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
            {
                std::cout << "shm creat done..." << std::endl;
                return true;
            }
        }
        return false;
    }
    bool GetShmForUser()
    {
        if (_who == gUser)
        {
            _shmid = GetCommShm(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid >= 0)
            {
                std::cout << "shm get done..." << std::endl;
                return true;
            }
        }
        return false;
    }
    void* GetShmAddr()
    {
        return _shmaddr;
    }
    void Zero()
    {
        if (_shmaddr)
        {
            memset(_shmaddr, 0, gShmSize);
        }
    }
    void GetShmAattribute()
    {
        shmid_ds ds;
        int res = shmctl(_shmid, IPC_STAT, &ds);
        if (res < 0)    return;
        std::cout << "ds.shm_perm.__key: " << ds.shm_perm.__key << std::endl;
        std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
    }
private:
    key_t _key;
    int _shmid;
    int _proj_id;
    int _who;
    void* _shmaddr;
    std::string _pathname;
};
